<?php
namespace app\common\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\helper\Time;
use mes\Mes;
use app\common\service\Order as OrderService;

/**
 * 定时同步发货订单
 * @author King
 *
 */
class CronSyncDeliveryOrder extends Command
{
	protected $logName = 'cronSyncDeliveryOrder';
	protected $mes = '';
	protected $deliveryStatus = '';
	protected $deliveryType = '';
	protected $returnStatus = '';
	protected $returnType = '';
	protected $forceUpdate = 0;
	protected $isPrint = 1;
	protected $history = 0;
	protected $tail = 0;

	protected function configure()
	{
		$this->setName('cronSyncDeliveryOrder')
		->addOption('mes', 'm', Option::VALUE_OPTIONAL, '来源类型(1小辉辉，2名贵坊，3十义)', 1)
		->addOption('print', 'p', Option::VALUE_OPTIONAL, '是否打印信息到终端(1是，0否)', 1)
		->addOption('force', 'f', Option::VALUE_OPTIONAL, '是否强制更新订单忽略hash校验(1是，0否)', 0)

		->addOption('client_ordernum', 'c', Option::VALUE_OPTIONAL, '订单号', '')		// 同步指定订单

		->addOption('time', 't', Option::VALUE_OPTIONAL, '当天前的第N天(如：1昨天，2前天)', 0)		// 同步某一天的订单

		->addOption('history', 'y', Option::VALUE_OPTIONAL, '是否同步历史订单(1是，0否)', 0)		// 同步最近40天的订单

		->addOption('tail', 'a', Option::VALUE_OPTIONAL, '是否每20分钟同步已打包未完成订单', 0)		// 每20分钟同步已打包未完成订单

		->setDescription('定时同步发货订单');
	}

	protected function execute(Input $input, Output $output)
	{
		set_time_limit(0);
		ini_set('memory_limit', '512M');
		error_reporting(E_ALL & ~E_NOTICE);

		$this->mes = $input->getOption('mes');
		$syncClientOrdernum = $input->getOption('client_ordernum');
		$time = $input->getOption('time');
		$this->forceUpdate = $input->getOption('force');
		$this->isPrint = $input->getOption('print');
		$this->history = $input->getOption('history');
		$this->tail = $input->getOption('tail');

		$this->logName .= $this->history ?  'History' : '';

		if (!in_array($this->mes, [1, 2, 3])) {
			$msg = "参数mes错误";
			if ($this->isPrint) consoleLog($msg);
			return ;
		}

		$this->deliveryStatus = array_flip(config('mimo.delivery_status'));
		$this->deliveryType = array_flip(config('mimo.delivery_type'));
		$this->returnStatus = array_flip(config('mimo.return_status'));
		$this->returnType = array_flip(config('mimo.delivery_type'));

		if ($this->isPrint) consoleLog('开始执行发货单同步程序');

		if ($syncClientOrdernum != '') {
			$this->logName .= '_order';
			// 同步指定订单
			$syncClientOrdernumArr = explode(',', $syncClientOrdernum);
			$orderList = db('order')->where('client_ordernum', 'in', $syncClientOrdernumArr)->where('status', '<>', 99)->field('is_auto_import,client_ordernum')->select();
			if ($orderList) {
				$orderGroupArr = [];
				foreach ($orderList as $v) {
					$orderGroupArr[$v['is_auto_import']][] = $v['client_ordernum'];
				}
				foreach ($orderGroupArr as $isAutoImport => $clientOrdernumArr) {
					$this->mes = $isAutoImport;
					$msg = "开始同步订单...[订单号：".implode(",", $clientOrdernumArr)."]";
					$this->_consoleLog($msg, 'start_batch');

					$this->_doSync($clientOrdernumArr, 0, 0, $output);
					$this->_consoleLog("本批次发货单同步完成", 'end_batch');
				}

				$this->_consoleLog("发货单同步程序执行完成", 'end');
				$output->write(json_encode(['code' => 1, 'msg' => '发货单同步完成'], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
				return;
			} else {
				$msg = "订单号不存在";
				$this->_consoleLog($msg, 'error');

				$output->write(json_encode(['code' => 0, 'msg' => $msg], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
				return ;
			}
		} elseif ($time > 0) {
			$this->logName .= '_someday';
			// 同步某一天的订单
			list($startTime, $endTime) = Time::today();
			$startTime = $startTime - $time*86400;
			$endTime = $endTime - $time*86400;

			$orderList = db('order')->where('accept_time', 'between', [$startTime, $endTime])->where('is_auto_import', $this->mes)->where('status', '<>', 99)->column('client_ordernum');
			$orderIdList = array_chunk($orderList, 100);
			foreach ($orderIdList as $k => $orderIds) {
				$msg = "开始同步订单...[".date('Y-m-d', $startTime)." 第".($k+1)."批，订单号：".implode(',', $orderIds)."]";
				$this->_consoleLog($msg, 'start_batch');

				$this->_doSync($orderIds, 0, 0, $output);
				$this->_consoleLog("本批次发货单同步完成", 'end_batch');
			}
			$this->_consoleLog("发货单同步程序执行完成", 'end');
			return;
		} elseif ($this->history == 1) {
			$this->logName .= '_40days';
			// 同步最近40天的订单发货信息（不含当天）
			$endTime = strtotime(date('Y-m-d 00:00:00', time()));
			$startTime = $endTime-40*86400;

			$orderList = db('order')->where('accept_time', 'between', [$startTime, $endTime])->where('is_auto_import', $this->mes)->where('status', '<>', 99)->where('tail_status', 1)->order('accept_time desc')->column('client_ordernum');
			$clientOrdernumList = array_chunk($orderList, 100);
			foreach ($clientOrdernumList as $k => $clientOrdernums) {
				$msg = "开始同步第".($k+1)."批，订单号：".implode(',', $clientOrdernums);
				$this->_consoleLog($msg, 'start_batch');

				$this->_doSync($clientOrdernums, 0, 0, $output);
				$this->_consoleLog("第".($k+1)."批发货单同步完成", 'end_batch');
			}
			$this->_consoleLog("发货单同步程序执行完成", 'end');

		} elseif ($this->tail) {
			$this->logName .= '_20min';
			// 每60分钟同步 车间已完成 的订单（早上9点-22点）
			if (intval(date('H')) >= 9 && intval(date('H')) < 22) {
				$orderList = db('order')->where('delivery_sync_status', '<>', 2)->where('status', 1)->where('chejian_status', 1)->where('is_auto_import', $this->mes)->column('client_ordernum');
				$clientOrdernumList = array_chunk($orderList, 100);
				foreach ($clientOrdernumList as $k => $clientOrdernums) {
					$msg = "开始同步第".($k+1)."批，订单号：".implode(',', $clientOrdernums);
					$this->_consoleLog($msg, 'start_batch');

					$this->_doSync($clientOrdernums, 0, 0, $output);
					$this->_consoleLog("第".($k+1)."批发货单同步完成", 'end_batch');
				}

				$this->_consoleLog("发货单同步程序执行完成", 'end');
				return;
			} else {
				$this->_consoleLog("未到可执行时间", 'warn');
			}
		} else {
			$this->logName .= '_3min';
			// 每3分钟同步已打包的订单（早上8点开始）
			$this->forceUpdate = true;
			if (intval(date('H')) >= 8) {
				$orderList = db('order')->where('delivery_sync_status', 2)->where('is_auto_import', $this->mes)->column('client_ordernum');
				$clientOrdernumList = array_chunk($orderList, 100);
				foreach ($clientOrdernumList as $k => $clientOrdernums) {
					$msg = "开始同步第".($k+1)."批，订单号：".implode(',', $clientOrdernums);
					$this->_consoleLog($msg, 'start_batch');

					$this->_doSync($clientOrdernums, 0, 0, $output);
					$this->_consoleLog("第".($k+1)."批发货单同步完成", 'end_batch');
				}

				$this->_consoleLog("发货单同步程序执行完成", 'end');
				return;
			} else {
				$this->_consoleLog("未到可执行时间", 'warn');
			}
		}
	}

	/**
	 * 执行同步及入库
	 *
	 * @author King
	 * @date 2021年2月6日
	 *
	 * @param array $produceOrderIdList		多个订单号
	 * @param integer $startTime			接单开始时间戳
	 * @param integer $endTime				接单结束时间戳
	 * @param Output $output
	 * @throws \Exception
	 */
	private function _doSync($produceOrderIdList, $startTime=0, $endTime=0, Output $output=null) {
		$startTimeStr = $startTime?date('Y-m-d H:i:s', $startTime):0;
		$endTimeStr = $endTime?date('Y-m-d H:i:s', $endTime):0;

		try {
			$mes = $this->mes;
			$mesLib = new Mes();

			$config = [
				'private_key' => env("mes{$mes}.private_key"),
				'app_id' => env("mes{$mes}.app_id"),
				'app_secure' => env("mes{$mes}.app_secure"),
				'host' => env("mes{$mes}.host"),
				'debug' => env("mes{$mes}.debug"),
			];
			$mesLib->setConfig($config);
			$result = $mesLib->getDeliverOrderList($produceOrderIdList, $startTimeStr, $endTimeStr);
            addLog($result, $this->logName . $this->mes.'_sam', true);
			$response = json_decode($result, true);
			// dump($response);

			if ($response['code'] == 0) {
				$list = $response['info'];

				$deliverNumberCount = count($list);
				$msg = "本次发现发货单".count($list)."个, deliverNumber:" . implode(',', array_column($list, 'deliverNumber'));
				$this->_consoleLog($msg, 'deliver_number');
				$this->_consoleLog(json_encode($list), 'deliver_list');

				$orderService = new OrderService();
				$orderIdUpdate = [];	// 需要更新的订单ID（即有发货单）
				$clientOrdernumUpdate = [];	// 需要更新的订单号（即有发货单）
				foreach ($list as $k => $d) {
					$deliverNumber = $d['deliverNumber']; // 发货单号
					$clientOrdernum = $d['produceOrderId'];

					// redis缓存检测
					$cacheKeyMd5 = md5(json_encode($d));
					$key = ORDER_DELIVERY . $deliverNumber;
					if (cache('?'. $key) && cache($key) == $cacheKeyMd5 && !$this->forceUpdate) {
						$this->_consoleLog('.', '', false);
						if($k+1 == $deliverNumberCount) $this->_consoleLog("\n", '', false);
						continue;
					}

					cache($key, $cacheKeyMd5, 10*86400);

					try {
						$orderInfo = $orderService->getInfoByClientOrdernum($clientOrdernum, 'factory_id,id,stock_type');
						if (empty($orderInfo)) throw new \Exception('未找到订单号'.$clientOrdernum, 404);

						// 检测发货单是否已存在
						$deliveryOrderInfo = db('order_delivery')->where('delivery_number', $deliverNumber)->field('id')->find();
						$deliveryData = [
							'factory_id' => $orderInfo['factory_id'],
							'order_id' => $orderInfo['id'],
							'client_ordernum' => $clientOrdernum,
							'sub_client_ordernum' => $d['subProduceOrderCode'],
							'stock_type' => $orderInfo['stock_type'],
							'delivery_time' => isset($d['sendTime']) && $d['sendTime'] ? strtotime($d['sendTime']) : 0,
							'delivery_count' => $d['deliveryQty'],
							'delivery_number' => $deliverNumber,
							'delivery_status' => arrayGet($this->deliveryStatus, $d['deliverStatusStr'], 0),
							'delivery_type' => arrayGet($this->deliveryType, $d['reasonStr'], 0),
							'check_time' => isset($d['inspectionTime']) && $d['inspectionTime'] ? strtotime($d['inspectionTime']) : 0,
							'return_time' => isset($d['returnTime']) && $d['returnTime'] ? strtotime($d['returnTime']) : 0,
							'return_count' => arrayGet($d, 'returnQty', 0),
							'return_status' => arrayGet($this->returnStatus, arrayGet($d, 'returnStatusStr', 0), 0),
							'return_type' => arrayGet($this->returnType, arrayGet($d, 'returnTypeStr', 0), 0),
							'store_time' => isset($d['storeTime']) && $d['storeTime'] ? strtotime($d['storeTime']) : 0,
							'store_count' => isset($d['storeInfo']) && $d['storeInfo'] ? array_sum(array_column($d['storeInfo'], 'quantity')) : 0,
							'delivery_detail' => isset($d['deliveryInfo']) && $d['deliveryInfo'] ? json_encode($d['deliveryInfo']) : '',
							'return_detail' => isset($d['returnInfo']) && $d['returnInfo'] ? json_encode($d['returnInfo']) : '',
							'store_detail' => isset($d['storeInfo']) && $d['storeInfo'] ? json_encode($d['storeInfo']) : '',
                            'pack_dai_time' => $d['deliverStatusStr'] == '待打包' ? time() : 0,
                            'pack_finish_time' => $d['deliverStatusStr'] == '已打包' ? time() : 0,
							'update_time' => time(),
						];
						if ($deliveryOrderInfo) {
							db('order_delivery')->where('id', $deliveryOrderInfo['id'])->update($deliveryData);
							$this->_consoleLog('*', '', false);
						} else {
							// 实际出货数默认=发货数
							$deliveryData['real_shipment_detail'] = $deliveryData['delivery_detail'];
							$deliveryData['real_shipment_count'] = $deliveryData['delivery_count'];
							$deliveryData['create_time'] = time();
							db('order_delivery')->insertGetId($deliveryData);
							$this->_consoleLog('+', '', false);
						}

						if($k+1 == $deliverNumberCount) $this->_consoleLog("\n", '', false);
						$orderIdUpdate[] = $orderInfo['id'];
						$clientOrdernumUpdate[] = $clientOrdernum;
					} catch (\Exception $e) {
						$msg = "发货单{$deliverNumber}更新失败，".$e->getMessage() . "，data:".json_encode($deliveryData);
						$this->_consoleLog($msg, 'error');
						cache($key, null);
					}
				}

				$msg = "开始更新订单及尺码相关数据";
				$this->_consoleLog($msg, 'update_order_size');

				// 更新订单及尺码数据
				$orderIdAllOver = []; // 订单的所有发货单全部入仓
				$orderIdUpdate = array_unique($orderIdUpdate);
				foreach ($orderIdUpdate as $orderId) {
					$orderDeliveryInfo = db('order_delivery')->where('order_id', $orderId)->field('min(check_time) check_time,min(store_time) store_time,min(return_time) return_time,min(delivery_status) delivery_status,sum(return_count) return_count,sum(store_count) store_count,sum(delivery_count) delivery_count')->findOrEmpty();
					if ($orderDeliveryInfo) {
                        $orderFinish=db('order_finish')->where('order_id', $orderId)->find();
					    if(isset($orderFinish['people_in_store_count']) && $orderFinish['people_in_store_count']>0){
					        unset($orderDeliveryInfo['store_count']); //不再覆盖已入工维护的入仓数
                        }
						$orderData = $orderDeliveryInfo;
						$orderData['update_time'] = time();
						$orderData['delivery_sync_status'] = 3;
						$orderData['delivery_sync_time'] = time();
						db('order')->where('id', $orderId)->update($orderData);
					}

					// 统计各尺码的发货数、退货数、入库数（尺码的入库数不准）
					$deliveryList = db('order_delivery')->where('order_id', $orderId)->where('delivery_status', '<>', 99)->field('delivery_detail,return_detail,store_detail,real_shipment_detail,delivery_status')->select();
					$deliveryCountMap = $returnCountMap = $storeCountMap = $realShipmentCountMap = [];
					foreach ($deliveryList as $v) {
						if($v['delivery_detail'] != '' && !in_array($v['delivery_status'], [6, 7])) {
							$tmp = json_decode($v['delivery_detail'], true);
							foreach ($tmp as $sv) {
								if (isset($deliveryCountMap[$sv['sizeName']])) $deliveryCountMap[$sv['sizeName']] += $sv['quantity'];
								else $deliveryCountMap[$sv['sizeName']] = $sv['quantity'];
							}
						}

						if($v['return_detail'] != '') {
							$tmp = json_decode($v['return_detail'], true);
							foreach ($tmp as $sv) {
								if (isset($returnCountMap[$sv['sizeName']])) $returnCountMap[$sv['sizeName']] += $sv['quantity'];
								else $returnCountMap[$sv['sizeName']] = $sv['quantity'];
							}
						}

						if($v['store_detail'] != '' && !in_array($v['delivery_status'], [6, 7])) {
							$tmp = json_decode($v['store_detail'], true);
							foreach ($tmp as $sv) {
								if (isset($storeCountMap[$sv['sizeName']])) $storeCountMap[$sv['sizeName']] += $sv['quantity'];
								else $storeCountMap[$sv['sizeName']] = $sv['quantity'];
							}
						}

						if($v['real_shipment_detail'] != '' && !in_array($v['delivery_status'], [6, 7])) {
							$tmp = json_decode($v['real_shipment_detail'], true);
							foreach ($tmp as $sv) {
								if (isset($realShipmentCountMap[$sv['sizeName']])) $realShipmentCountMap[$sv['sizeName']] += $sv['quantity'];
								else $realShipmentCountMap[$sv['sizeName']] = $sv['quantity'];
							}
						}
					}

					// 重新统计订单各尺码的 发货数/打包数
					foreach ($deliveryCountMap as $sizeName => $count) {
						db('order_size')->where('order_id', $orderId)->where('size_name', $sizeName)->update(['delivery_count' => $count, 'jiaohuo_tail_count' => $count, 'update_time' => time()]);
					}
					// 重新统计订单各尺码的 退货数
					foreach ($returnCountMap as $sizeName => $count) {
						db('order_size')->where('order_id', $orderId)->where('size_name', $sizeName)->update(['return_count' => $count, 'update_time' => time()]);
					}
					// 重新统计订单各尺码的 入仓数
					foreach ($storeCountMap as $sizeName => $count) {
						db('order_size')->where('order_id', $orderId)->where('size_name', $sizeName)->update(['store_count' => $count, 'update_time' => time()]);
					}
					// 重新统计订单各尺码的 实际出货数
					foreach ($realShipmentCountMap as $sizeName => $count) {
						db('order_size')->where('order_id', $orderId)->where('size_name', $sizeName)->update(['daohuo_count' => $count, 'update_time' => time()]);
					}

					// 重新统计订单总的 实际出货数
					$realShipmentCount = array_sum($realShipmentCountMap);
					if ($realShipmentCount > 0) {
						$realShipmentTime = db('order_delivery')->where('order_id', $orderId)->where('delivery_status', 'not in', [6, 7])->min('delivery_time');
						db('order')->where('id', $orderId)->update(['real_shipment_status' => 1, 'real_shipment_count' => $realShipmentCount, 'real_shipment_time' => $realShipmentTime, 'update_time' => time()]);
					} else {
						db('order')->where('id', $orderId)->update(['real_shipment_status' => 0, 'real_shipment_count' => 0, 'real_shipment_time' => 0, 'update_time' => time()]);
					}

					// 尾部自动完成，判断条件 MES的子单状态是 全部是 已打包
					$deliveryStatInfo = db('order_delivery')->where('order_id', $orderId)->field('min(delivery_status) as min_delivery_status,count(delivery_status) as count_delivery_status')->find();
					if ($deliveryStatInfo && $deliveryStatInfo['min_delivery_status'] != 1 && $deliveryStatInfo['count_delivery_status'] > 0) {
						$tailStatus = db('order')->where('id', $orderId)->value('tail_status', 0);
						if ($tailStatus == 0) {
							db('order')->where('id', $orderId)->update(['tail_status' => 1, 'tail_over_time' => time(), 'update_time' => time()]);
							$msg = "订单{$orderId}发货单全部已打包，订单标记尾部完成";
							$this->_consoleLog($msg, 'update_order');

							$this->_addOrderLog($orderId, '', '【同步发货单】全部已打包，标记尾部完成');
						}
					}

					// 全部入仓，则标记订单完成
					$deliveryNormalList = db('order_delivery')->where('order_id', $orderId)->where('delivery_status', 'not in', [6,7,99])->column('delivery_status');
					if ($deliveryNormalList) {
						$isOver = true;
						foreach ($deliveryNormalList as $ds) {
							if ($ds != 8) {
								$isOver = false;
								break;
							}
						}
						if ($isOver) {
							$orderIdAllOver[] = $orderId;
						}
					}

					$this->_consoleLog('*', '', false);
				}
				if($orderIdUpdate) $this->_consoleLog("\n", '', false);

				if ($orderIdAllOver) {
					db('order')->where('id', 'in', $orderIdAllOver)->update(['status' => 2, 'over_time' => time(), 'update_time' => time()]);
					$msg = "订单".implode(',', $orderIdAllOver)."发货单全部完成，订单标记完成";
					$this->_consoleLog($msg, 'update_order');

					foreach ($orderIdAllOver as $cOrderId) {
						$this->_addOrderLog($cOrderId, '', '【同步发货单】全部已入仓，标记订单完成');
					}

                    $clientOrdernums = db('order')->where('id', 'in', $orderIdAllOver)->column('client_ordernum');
                    $redis = getRedis();
                    // 将该订单从在途订单缓存中移除
                    foreach ($clientOrdernums as $clientOrdernum) {
                        $redis->hDel(ORDER_ING, $clientOrdernum);
                    }
				}

				// 标记还没有发货单的订单
				if (!$this->history) {
					$clientOrdernumTmp = array_unique(array_diff($produceOrderIdList, $clientOrdernumUpdate));
					if ($clientOrdernumTmp) {
						db('order')->where('client_ordernum', 'in', $clientOrdernumTmp)->where('status', '<>', 99)->update(['delivery_sync_status' => 1, 'delivery_sync_time' => time()]);
					}
				}

				$msg = "更新订单及尺码相关数据完成";
				$this->_consoleLog($msg, 'update_order_size');
			} else {
				throw new \Exception($response['msg']);
			}

		} catch (\Exception $e) {
			$msg = "错误:".$e->getMessage();
			$this->_consoleLog($msg, 'Exception');
		}
	}

	/**
	 * 记录日志并打印
	 *
	 * @author KING
	 * @date 2020年6月11日
	 *
	 * @param string $msg
	 * @param string $flag
	 * @param string $format
	 */
	private function _consoleLog($msg, $flag, $format=true) {
		if ($this->isPrint) consoleLog($msg, $flag, $format);
		addLog($msg, $this->logName . $this->mes, $format);
	}

	/**
	 * 添加订单日志
	 *
	 * @author KING
	 * @date 2020年7月25日
	 *
	 * @param integer $orderId			订单ID
	 * @param string $clientOrdernum	订单号
	 * @param string $remark			备注
	 * @return boolean
	 */
	private function _addOrderLog($orderId, $clientOrdernum, $remark) {
		if (empty($orderId) && empty($clientOrdernum)) return false;
		if (empty($orderId)) {
			$orderId = db('order')->where('client_ordernum', $clientOrdernum)->value('id', 0);
		} elseif (empty($clientOrdernum)) {
			$clientOrdernum = db('order')->where('id', $orderId)->order('id desc')->value('client_ordernum', '');
		}
		$logData = [
			'uid' => 0,
			'order_id' => $orderId,
			'client_ordernum' => $clientOrdernum,
			'url' => '',
			'module' => request()->module(),
			'controller' => request()->controller(),
			'action' => request()->action(),
			'param' => '',
			'remark' => $remark,
			'ip' => request()->ip(),
			'create_time' => time(),
		];
		db('order_log')->insert($logData);
		return true;
	}
}